<?php

namespace app\common\model\banxi\course;

use app\common\enums\LexiconStatusEnum;
use app\common\enums\WordStatusEnum;
use app\common\model\banxi\word\WordDictionary;
use app\common\model\common\BaseModel;
use app\common\service\word\WordUtilsService;
use ba\Exception;
use think\db\exception\PDOException;
use think\exception\ValidateException;
use think\facade\Cache;
use think\model\concern\SoftDelete;

/**
 * 课程字典
 */
class Lexicon extends BaseModel
{
    use SoftDelete;

    protected $name = 'banxi_lexicon';

    protected $autoWriteTimestamp = true;

    protected $deleteTime = 'del';

    protected $defaultSoftDelete = 0;

    protected $append = [
        'statusCn',
        'realStatusCn'
    ];

    protected $type = [
        'id' => 'string',
        'del' => 'string',
        'courseId' => 'string',
        'createTime' => 'timestamp:Y-m-d H:i:s',
        'updateTime' => 'timestamp:Y-m-d H:i:s',
    ];

    //-----------------------------------------------------------------------------------
    //｜                                   获取器                                         ｜
    //-----------------------------------------------------------------------------------

    public function getStatusCnAttr($value, $data)
    {
        $value = $value ?: ($data['status'] ?? '');
        return (new LexiconStatusEnum)->getDescription($value) ?: '';
    }

    public function getRealStatusCnAttr($value, $data)
    {

        $value = $value ?: ($data['realStatus'] ?? '');
        return (new LexiconStatusEnum)->getDescription($value) ?: '';
    }


    //-----------------------------------------------------------------------------------
    //｜                                   处理方法                                       ｜
    //----------------------------------------------------------------------------------


    /**
     * 根据课程ID和单词列表查询词库
     *
     * @param int $courseId 课程ID
     * @param array $words 单词列表
     * @return array 词库列表
     */
    public function listByCourseIdAndWords($courseId, $words)
    {
        // 如果单词列表为空，返回空数组
        if (empty($words)) {
            return [];
        }

        // 查询词库信息
        $lexiconList = $this
            ->where('courseId', $courseId)
            ->where('word', 'in', $words)
            ->order('id', 'asc')
            ->select()
            ->toArray();

        // 移除重复单词
        return $this->removeRepeatWord($lexiconList);
    }

    /**
     * 移除重复单词
     *
     * @param array $lexiconList 词库列表
     * @return array 去重后的词库列表
     */
    private function removeRepeatWord($lexiconList)
    {
        $uniqueWords = [];
        $result = [];

        foreach ($lexiconList as $lexicon) {
            // 如果该单词尚未添加到结果中，则添加
            if (!in_array($lexicon['word'], $uniqueWords)) {
                $uniqueWords[] = $lexicon['word'];
                $result[] = $lexicon;
            }
        }

        return $result;
    }

    public function listBaseInfoByCourseAndWords($courseId, $wordList)
    {
        if (empty($wordList)) {
            return [];
        }
        return $this->when($courseId, function ($query) use ($courseId) {
            $query->where('courseId', $courseId);
        })->whereIn('word', $wordList)
            ->order(['id' => 'asc'])
            ->select();
    }

    /**
     * 根据课程ID和排除的单词列表查询词库记录
     */
    public function listByCourseIdAndNotWords($page, $limit, $courseId, $words)
    {
        // 构建查询
        $db = $this->where('courseId', $courseId);

        // 如果单词列表非空，则添加排除条件
        if (!empty($words)) {
            $db->whereNotIn('word', $words);
        }
        return $db->order('id', 'asc')->paginate([
            'page' => $page,
            'list_rows' => $limit,
        ]);
    }

    public function getListPage(array $where, string $page = "1", int $limit = 10)
    {
        return $this->with([
            'dictInfo' => function ($query) {
                $query->withoutField(['phoneticSplit', 'metaVowel', 'metaConsonant', 'exampleSentence']);
            }
        ])->where($where)
            ->order('id', 'asc')
            ->paginate([
                'page' => $page,
                'list_rows' => $limit,
            ]);
    }

    public function getInfo(array $where)
    {
        return $this->with([
            'dictInfo' => function ($query) {
                $query->hidden(['phoneticSplit', 'metaVowel', 'metaConsonant', 'exampleSentence']);
            }
        ])->where($where)
            ->order('id', 'asc')
            ->find();
    }

    public function del($id)
    {
        $lexicon = $this->where(['id' => $id])->find();
        if (!$lexicon) {
            return true;
        }
        $this->startTrans();
        try {
            $lexicon->delete();
            $this->refreshCourseCount($lexicon['courseId']);
            $this->commit();
        } catch (Exception|PDOException|ValidateException $e) {
            $this->rollback();
            throw new ValidateException($e->getMessage());
        }
        return true;
    }

    public function dels(array $ids, string|int $courseId)
    {
        if (empty($ids)) {
            return true;
        }
        $this->startTrans();
        try {
            self::destroy(function ($query) use ($ids) {
                $query->whereIn('id', $ids);
            });
            $this->refreshCourseCount($courseId);
            $this->commit();
        } catch (Exception|PDOException|ValidateException $e) {
            $this->rollback();
            throw new ValidateException($e->getMessage());
        }
        return true;

    }

    public function saveOrUpdate(array $data)
    {
        $this->startTrans();
        try {
            $courseId = $data['courseId'];
            $this->lockSaveOrRemove($courseId, function ($maxSeq) use ($data) {
                $id = $data['id'] ?? null;
                $lexicon = $data;
                if ($id != null) {
                    $lexiconRaw = $this->where(['id' => $id])->find();
                    if (!$lexiconRaw) {
                        throw new ValidateException('词汇不存在');
                    }
                    $word = $lexiconRaw['word'];
                    if ($word != $lexicon['word']) {
                        $lexicon['status'] = LexiconStatusEnum::E3;
                        $lexicon['wordAudio'] = "";
                    } else {
                        $lexicon['status'] = $lexicon['realStatus'];
                    }
                } else {
                    $lexicon['status'] = LexiconStatusEnum::E3;
                }
                $lexicon['firstLetter'] = WordUtilsService::getFirstLetter($lexicon['word']);
                if ($this->getExistWord($lexicon['courseId'], $lexicon['word'], $id)) {
                    throw new ValidateException('词汇已存在');
                }
                $dictionaryModel = new WordDictionary();
                $wordDictionary = $dictionaryModel->getByWord($data['word']);
                if ($wordDictionary) {
                    $lexicon['phonetic'] = WordUtilsService::getFilterPhonetic($wordDictionary['phonetic']);
                    $lexicon['translation'] = $wordDictionary['translation'];
                    $wordAudio = $wordDictionary['wordAudio'];
                    if ($wordAudio) {
                        $lexicon['wordAudio'] = $wordAudio;
                        $lexicon['status'] = $lexicon['realStatus'];
                    }
                } else {
                    $dictionaryModel->refreshDictInfo([
                        'translation' => '',
                        'phonetic' => '',
                        'word' => $lexicon['word'],
                        'firstLetter' => $lexicon['firstLetter'],
                        'isSynthesis' => 0,
                        'status' => WordStatusEnum::E1
                    ], false);
                }
                if ($id != null) {
                    $lexiconRaw->save($lexicon);
                } else {
                    $this->refreshCourseCountByNum($lexicon['courseId'], $maxSeq + 1);
                    $this->save($lexicon);
                }
            });
            $this->commit();
        } catch (Exception|PDOException|ValidateException $e) {
            $this->rollback();
            throw new ValidateException($e->getMessage());
        }
        return true;
    }

    public function lockSaveOrRemove(string $courseId, $consumer)
    {
        $lockKey = "Lexicon:saveOrRemove:" . $courseId;
        if (Cache::has($lockKey)) {
            throw new  ValidateException('操作冲突，请稍后重试');
        }
        Cache::set($lockKey, 1, 5);
        try {
            $courseCount = $this->where(['courseId' => $courseId])->count();
            $consumer($courseCount);
        } catch (Exception|PDOException|ValidateException $e) {
            throw new ValidateException($e->getMessage());
        } finally {
            Cache::delete($lockKey);
        }
        return true;
    }

    public function getExistWord(string|int $courseId, string $word, string|int $id = null)
    {
        return $this->where([
            'courseId' => $courseId,
            'word' => $word
        ])->when($id != null, function ($query) use ($id) {
            $query->where('id', '<>', $id);
        })->count();
    }

    public function refreshCourseCount($courseId)
    {
        $num = $this->where(['courseId' => $courseId])->count();
        $this->refreshCourseCountByNum($courseId, $num);

    }

    public function refreshCourseCountByNum(string|int $courseId, string|int $num)
    {
        $courseModel = new Course();
        return $courseModel->refreshCourseDictCount($courseId, $num);
    }

    public function updateTranslationByWord(string $word, string $translation, string $phonetic)
    {
        return $this->update([
            'translation' => $translation,
            'phonetic' => WordUtilsService::getFilterPhonetic($phonetic),
            'firstLetter' => WordUtilsService::getFirstLetter($word)
        ], ['word' => $word]);
    }

    public function updateAudioByWord(string $word, string $audioUrl)
    {
        return $this->update([
            'wordAudio' => $audioUrl,
            'status' => LexiconStatusEnum::E1
        ], [
            'word' => $word,
            'status' => LexiconStatusEnum::E3
        ]);
    }

    public function importExcel($courseId, $importExcelList)
    {
        $this->startTrans();
        try {
            $this->lockSaveOrRemove($courseId, function ($maxSeq) use ($courseId, $importExcelList) {
                $lexiconList = [];
                $wordSet = [];
                $importExcelListCount = count($importExcelList);
                for ($i = 0; $i < $importExcelListCount; $i++) {
                    $it = $importExcelList[$i];
                    $lexicon = [
                        'word' => $it,
                        'courseId' => $courseId,
                        'status' => LexiconStatusEnum::E3,
                        'realStatus' => LexiconStatusEnum::E1,
                    ];
                    $lexicon['firstLetter'] = WordUtilsService::getFirstLetter($lexicon['word']);
                    $lexiconList[] = $lexicon;
                }
                $lexiconListCount = count($lexiconList);
                for ($i = 0; $i < $lexiconListCount; $i++) {
                    $msg = "第" . ($i + 1) . "行";
                    $lexicon = $lexiconList[$i];
                    $word = $lexicon['word'];
                    if (empty($word)) {
                        throw new ValidateException("{$msg}词汇不能为空");
                    }
                    if (in_array($word, $wordSet)) {
                        throw new ValidateException("单词：{$word}已存在，请检查您的文件中是否存在多个$word");
                    } else {
                        $wordSet[] = $word;
                    }
                }
                $wordDictionaryModel = new WordDictionary();
                $wordDictionaryList = $wordDictionaryModel->listByWord($wordSet)->toArray();
                $wordDictionaryListColumn = array_column($wordDictionaryList, null, 'word');
                $addWordDictionaryList = [];
                foreach ($lexiconList as $lexicon) {
                    $wordDictionary = $wordDictionaryListColumn[$lexicon['word']] ?? null;
                    if ($wordDictionary != null) {
                        $lexicon['phonetic'] = $wordDictionary['phonetic'];
                        $lexicon['translation'] = $wordDictionary['translation'];
                        $wordAudio = $wordDictionary['wordAudio'];
                        if ($wordAudio) {
                            $lexicon['wordAudio'] = $wordDictionary['wordAudio'];
                            $lexicon['status'] = LexiconStatusEnum::E1;
                        }
                    } else {
                        $addWordDictionaryList[] = [
                            'word' => $lexicon['word'],
                            'firstLetter' => $lexicon['firstLetter'],
                            'isSynthesis' => 0,
                            'status' => LexiconStatusEnum::E1,
                        ];
                    }
                    $info = $this->where([
                        'word' => $lexicon['word'],
                        'courseId' => $courseId
                    ])->find();
                    if (!$info) {
                        $info = new self();
                    }
                    $info->save($lexicon);
                }
                if ($addWordDictionaryList) {
                    $wordDictionaryModel->saveAll($addWordDictionaryList);
                }
                $this->refreshCourseCountByNum($courseId, $maxSeq + count($lexiconList));
            });
            $this->commit();
        } catch (Exception|PDOException|ValidateException $e) {
            $this->rollback();
            throw new ValidateException($e->getMessage());
        }
        return true;
    }

    public function removeByWord($word)
    {
        return self::destroy(function ($query) use ($word) {
            $query->where(['word' => $word]);
        });
    }

    //-----------------------------------------------------------------------------------
    //｜                                   模型关联                                       ｜
    //-----------------------------------------------------------------------------------

    public function dictInfo()
    {
        return $this->hasOne(WordDictionary::class, 'word', 'word');
    }

}